package org.jvm.instruction.references.invokemethod;

import org.jvm.instruction.base.*;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.*;
import org.jvm.rtda.heap.classmember.Method;
import org.jvm.rtda.heap.symref.MethodRef;
import org.jvm.rtda.thread.Frame;

/**
 * 实例中调用private方法，super关键字调用，构造方法调用。不需要动态绑定的场合使用
 * 使用super关键字调用实例方法时，jvm会直接使用此指令指定被调用方法字面类为实例的父类，从而在父类中找到要执行的方法
 *
 * @author 海燕
 * @date 2023/2/7
 */
public class INVOKE_SPECIAL extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        MethodRef methodRef = (MethodRef) frame.getMethod().getKlass().getConstantPool().getConstant(this.index);
        //被调用方字面类型
        Klass resolvedClass = methodRef.resolvedClass(frame.getThread());
        //实际将要执行的方法
        Method resolvedMethod = methodRef.resolveMethodRef(frame.getThread());
        //被调用方法实际所在类
        Klass infactClass = resolvedMethod.getKlass();
        //调用方，指令实际执行所在类
        Klass currentClass = frame.getMethod().getKlass();
        //实例的构造方法必须由实例本类发起调用
        if (resolvedMethod.getName().equals("<init>") && infactClass != resolvedClass) {
            throw new RuntimeException("init method error");
        }
        //实例方法不应是静态
        if (resolvedMethod.isStatic()) {
            throw new RuntimeException("method is static");
        }
        //获取实例引用
        Object ref = frame.getOperandStack().getRefFromTop(resolvedMethod.getArgSlotCount() - 1);
        if (ref == null) {
            frame.getThread().throwNullPointerException();
            return;
        }
        /**
         * protect方法虽然允许不同包中的子类访问，但其实不允许子类通过父类字面引用访问protect方法
         * 仅允许子类访问继承自父类的protect方法，或者通过super关键字访问的方法
         * 这里的代码是在校验底部注释的情况
         */
        if (resolvedMethod.isProtected()
                && infactClass.isSuperClassOf(currentClass)
                && !currentClass.getPackageName().equals(infactClass.getPackageName())
                && ref.getKlass() != currentClass
                && !ref.getKlass().isSubClassOf(currentClass)) {
            throw new RuntimeException("method is protected");
        }
        //判断acc_super标识
        Method methodToBeInvoked = resolvedMethod;
        if (currentClass.isSuper()
                && resolvedClass.isSuperClassOf(currentClass)
                && !resolvedMethod.getName().equals("<init>")) {
            methodToBeInvoked = MethodUtil.lookupMethodInClass(currentClass.getSuperClass(), methodRef.getName(), methodRef.getDescriptor());
        }
        //不能是抽象方法
        if (methodToBeInvoked.isAbstract()) {
            throw new RuntimeException("method is abstract");
        }
        //开始方法调用
        InstructionUtil.invokeMethod(frame, methodToBeInvoked);
    }
}
//类A在包a中，类B在包b中，类C在包c中。三个类都不同包
//像下面这种在B中创建C的实例，并调用A的方法是不行的
//
//public class A {
//    protected int geti() {
//        return 1;
//    }
//}
//public class C extends A {
//}
//public class B extends A {
//    void test(){
//        A a=new C();
//        a.geti();
//    }
//}